﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;
using mmCore.Classes;
using mmCore.Common;
using mmFilestubeExtension;
using mmImdbProvider;
using mmJdownloaderExtension;
using mmSubProvider;
using mmUI.Managers;
using mmUI.Properties;
using mmUI.UiUtils;

namespace mmUI.ViewModels
{
    public class MainWindowVm : NotifyPropertyChangedBase
    {
        #region MEMBERS

        private Movie _mSelectedMovie = null;
        private Episode _mSelectedEpisode = null;
        private bool _mLoadingEpisodes = false;
        private readonly ObservableCollection<Episode> _mEpisodes = new ObservableCollection<Episode>();
        private readonly ObservableCollection<SearchMovieResult> _mSearchResult = new ObservableCollection<SearchMovieResult>();
        private readonly Dispatcher _mDispatcher;
        private SearchMovieResult _mSelectedSearchResult = null;
        private string _mSearchString = string.Empty;
        private bool _mSliderEnabled = false;
        private bool _mIsSearching = false;
        private Movie _mMovieToAdd = null;
        private int _mReloaded = 0;

        #endregion MEMBERS

        #region CONSTRUCTOR

        public MainWindowVm(Dispatcher disp)
        {
            _mDispatcher = disp;
        }

        #endregion CONSTRUCTOR

        #region TOOLS

        public bool AllEpisodesSelected
        {
            get 
            {
                return Episodes != null ? Episodes.All(ep => ep.Seen) : false;
            }
            
            set
            {
                if(Episodes == null)
                {
                    return;
                }
                foreach(var ep in Episodes)
                {
                    ep.Seen = value;
                }
                RaisePropertyChanged(() => AllEpisodesSelected);
            }
        }

        #endregion TOOLS

        #region SEARCH

        public bool IsSearching
        {
            get { return _mIsSearching; }
            set
            {
                _mIsSearching = value;
                RaisePropertyChanged(() => IsSearching);
            }
        }

        public bool IsSearchResultSelected
        {
            get { return SelectedSearchResult != null; }   
        }

        public RelayCommand SearchCommand
        {
            get
            {
                return new RelayCommand((o) =>
                {
                    var dbs = new ImdbSearch();
                    IsSearching = true;
                    dbs.OnFound += dbs_OnFound;
                    if(dbs.Search(SearchString))
                    {
                        SliderEnabled = true;
                    }

                });
            }
        }

        public RelayCommand AddSelectedSearchResult
        {
            get
            {
                return new RelayCommand((o) =>
                {
                    if(SelectedSearchResult == null)
                    {
                        return;
                    }

                    if(MovieManager.Movies.Where(mov => mov.Id == SelectedSearchResult.Id).Count() != 0)
                    {
                        _mDispatcher.Invoke(new Action(() =>
                        {
                            SearchString = string.Empty;
                            SearchResult.Clear();
                        }), DispatcherPriority.DataBind);
                        return;
                    }

                    _mMovieToAdd = new Movie {Title = SelectedSearchResult.Title, Id = SelectedSearchResult.Id, Reloading = true};
                    _mDispatcher.Invoke(new Action(() => MovieManager.Movies.Add(_mMovieToAdd)), DispatcherPriority.DataBind);
                    var idd = new ImdbDetails();
                    idd.OnFinished += idd_OnFinished;
                    idd.Read(SelectedSearchResult);
                });
            }
        }

        void idd_OnFinished(Movie movie)
        {
            var dbd = new ImdbEpisodeReader();
            if(movie == null)
            {
                dbd_OnFinished(null);
                return;
            }
            UpdateMovieCover(movie);
            dbd.OnFinished += dbd_OnFinished;
            dbd.Read(movie);
        }

        void dbd_OnFinished(Movie movie)
        {
            _mDispatcher.Invoke(new Action(() =>
                                               {
                                                   if (movie == null)
                                                   {
                                                       MovieManager.Movies.Remove(_mMovieToAdd);
                                                   }
                                                   else
                                                   {
                                                       _mMovieToAdd.CoverLocal = movie.CoverLocal;
                                                       _mMovieToAdd.CoverWeb = movie.CoverWeb;
                                                       _mMovieToAdd.Episodes = movie.Episodes;
                                                       _mMovieToAdd.Season = movie.Season;
                                                       _mMovieToAdd.SeasonCount = movie.SeasonCount;
                                                       _mMovieToAdd.Reloading = false;
                                                       _mMovieToAdd = null;
                                                   }
                                                   
                                                   SearchString = string.Empty;
                                                   SearchResult.Clear();
                                               }), DispatcherPriority.DataBind);
        }

        void dbs_OnFound(List<SearchMovieResult> result)
        {
            if (result != null)
            {
                foreach (var sr in result)
                {
                    sr.CoverLocal = Path.Combine(Config.AppDir, Config.CoverDir, string.Format("{0}m.jpg", sr.Id));
                    if (!File.Exists(sr.CoverLocal))
                    {
                        Utils.Download(sr.CoverUrl, sr.CoverLocal);
                    }
                }
            }
            _mDispatcher.Invoke(new Action(() =>
            {
                SearchResult.Clear();
                if (result != null)
                {
                    foreach (var sr in result)
                    {
                        SearchResult.Add(sr);
                    }
                }
                IsSearching = false;
            }), DispatcherPriority.DataBind);
        }

        public string SearchString
        {
            set
            {
                _mSearchString = value;
                if(!string.IsNullOrEmpty(_mSearchString))
                {
                    
                }
                RaisePropertyChanged(() => SearchString);
            }
            get { return _mSearchString; }
        }

        public ObservableCollection<SearchMovieResult> SearchResult
        {
            get { return _mSearchResult; }
        }

        public SearchMovieResult SelectedSearchResult
        {
            set 
            { 
                _mSelectedSearchResult = value;
                RaisePropertyChanged(() => IsSearchResultSelected);
                RaisePropertyChanged(() => SelectedSearchResult);
            }

            get { return _mSelectedSearchResult; }
        }

        #endregion SEARCH

        public ObservableCollection<Movie> Movies
        {
            get { return MovieManager.Movies; }
        }

        public bool IsMovieSelected
        {
            get { return SelectedMovie != null; }
        }

        public Movie SelectedMovie
        {
            get { return _mSelectedMovie; }

            set
            {
                _mEpisodes.Clear();

                if (_mSelectedMovie != null)
                {
                    _mSelectedMovie.PropertyChanged -= SelectedMoviePropertyChanged;
                }

                _mSelectedMovie = value;

                if (_mSelectedMovie != null)
                {
                    _mSelectedMovie.PropertyChanged += SelectedMoviePropertyChanged;
                }

                if (_mSelectedMovie != null)
                {
                    SliderEnabled = true;

                    ReloadEpisodes();
                }
                else
                {
                    SliderEnabled = false;
                }

                RaisePropertyChanged(() => SelectedMovie);
                RaisePropertyChanged(() => CanReloadEpisodes);
                RaisePropertyChanged(() => Episodes);
                RaisePropertyChanged(() => NextEpisodeAirDate);
                RaisePropertyChanged(() => IsMovieSelected);
            }
        }

        private void SelectedMoviePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "Season")
            {
                ReloadEpisodes();
            }
        }

        private void ReloadEpisodes()
        {
            if (_mEpisodes.Count != 0)
            {
                foreach(var ep in _mEpisodes)
                {
                    ep.PropertyChanged -= ep_PropertyChanged;
                }
                _mEpisodes.Clear();
            }

            if (_mSelectedMovie.Episodes == null)
            {
                return;
            }

            var list = _mSelectedMovie.Episodes.ToList().Where(
                i => i.AirDate <= DateTime.Now && i.AirDate != DateTime.MinValue && i.Season == _mSelectedMovie.Season).
                OrderByDescending(i => i.AirDate);

            foreach (var ep in list)
            {
                ep.PropertyChanged += ep_PropertyChanged;
                _mEpisodes.Add(ep);
            }

            RaisePropertyChanged(() => AllEpisodesSelected);
        }

        void ep_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "Seen")
            {
                if(SelectedMovie != null)
                {
                    SelectedMovie.NotSeenTilToday = 0; // just raise notification, zero is not significant.
                    RaisePropertyChanged(() => AllEpisodesSelected);
                }
            }
        }

        public string NextEpisodeAirDate
        {
            get
            {
                if (_mSelectedMovie != null && _mSelectedMovie.Episodes != null)
                {
                    var ep = _mSelectedMovie.Episodes.ToList().Where(
                            i => i.AirDate > DateTime.Now).OrderBy(
                                i => i.AirDate).FirstOrDefault();
                    if(ep != null)
                    {
                        return ep.AirDate.ToShortDateString();
                    }
                }
                return "unknown";
            }
        }

        public ObservableCollection<Episode> Episodes
        {
            get
            {
                return _mEpisodes;
            }
        }

        public Episode SelectedEpisode
        {
            get { return _mSelectedEpisode; }

            set
            {
                _mSelectedEpisode = value;
                RaisePropertyChanged(() => SelectedEpisode);
            }
        }

        public bool SliderEnabled
        {
            get { return _mSliderEnabled; }
            set 
            { 
                _mSliderEnabled = value;
                RaisePropertyChanged(() => SliderEnabled);
            }
        }

        #region RELOADING

        public bool LoadingEpisodes
        {
            get { return _mLoadingEpisodes; }
        }

        public bool CanReloadEpisodes
        {
            get { return !LoadingEpisodes && SelectedMovie != null; }
        }

        public RelayCommand RemoveSelectedMovie
        {
            get
            {
                return new RelayCommand((arg) =>
                                            {
                                                if(SelectedMovie == null)
                                                {
                                                    return;
                                                }
                                                if(MessageBox.Show(string.Format("Are you really sure you want to remove movie series: \"{0}\"?", SelectedMovie.Title), 
                                                    Resources.RemoveSelectedMovie, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                                                {
                                                    return;
                                                }
                                                var movie = SelectedMovie;
                                                SelectedMovie = MovieManager.Movies.FirstOrDefault(m => m != movie);
                                                MovieManager.Movies.Remove(movie);
                                            });
            }
        }

        public RelayCommand ReloadSelectedMovieEpisodes
        {
            get
            {
                return new RelayCommand((arg) =>
                                            {
                                                if (SelectedMovie == null) return;
                                                var dbel = new ImdbEpisodeReader();
                                                SelectedMovie.Reloading = true;
                                                dbel.OnFinished += dbel_OnFinished;
                                                dbel.Read(SelectedMovie, true);
                                                _mLoadingEpisodes = true;
                                                _mEpisodes.Clear();
                                                RaisePropertyChanged(() => CanReloadEpisodes);
                                            });
            }
        }

        void dbel_OnFinished(Movie movie)
        {
            UpdateMovieCover(movie);
            _mDispatcher.Invoke(new Action(() =>
            {
                ReloadEpisodes();
                _mLoadingEpisodes = false;
                movie.Reloading = false;
                RaisePropertyChanged(() => CanReloadEpisodes);
            }), DispatcherPriority.DataBind);
        }

        public RelayCommand ReloadAllMovies
        {
            get
            {
                return new RelayCommand(o =>
                {
                    _mLoadingEpisodes = true;
                    RaisePropertyChanged(() => CanReloadEpisodes);
                    _mReloaded = 0;
                    foreach(var movie in Movies)
                    {
                        var dber = new ImdbEpisodeReader();
                        dber.OnFinished += dber_OnFinished;
                        movie.Reloading = true;
                        dber.Read(movie, true);
                    }
                });
            }
        }

        private static void UpdateMovieCover(Movie movie)
        {
            var file = Path.Combine(Config.AppDir, Config.CoverDir, string.Format("{0}.jpg", movie.Id));
            if (!File.Exists(file)) Utils.Download(movie.CoverWeb, file);
            movie.CoverLocal = file;
        }

        void dber_OnFinished(Movie movie)
        {
            UpdateMovieCover(movie);
            _mDispatcher.Invoke(new Action(() => 
            { 
                movie.Reloading = false;
                Interlocked.Increment(ref _mReloaded);
                if (_mReloaded == Movies.Count)
                {
                    _mLoadingEpisodes = false;
                    RaisePropertyChanged(() => CanReloadEpisodes);
                }
            }), DispatcherPriority.DataBind);
        }

        #endregion RELOADING

        #region EXTENSIONS

        public RelayCommand OpenJdownloader
        {
            get
            {
                return new RelayCommand(o =>
                {
                    var jd = new JdownloaderExtension();
                    jd.Execute(null, null, Config.JdownloaderExe);
                });
            }
        }

        public RelayCommand ExecuteFilestubeExtension
        {
            get
            {
                return new RelayCommand(o =>
                {
                    if (SelectedMovie == null || SelectedEpisode == null)
                    {
                        return;
                    }
                    var filestubeExt = new FilestubeExtension();
                    filestubeExt.Execute(SelectedMovie, SelectedEpisode, Config.FilestubeArg);
                });
            }
        }

        public RelayCommand DownloadSubtitles
        {
            get
            {
                return new RelayCommand((o) =>
                {
                    if (SelectedMovie == null || SelectedEpisode == null)
                    {
                        return;
                    }
                    var sp = new SubProvider();
                    sp.OnFound += sp_OnFound;
                    sp.FindSubtitle(SelectedMovie.Title, SelectedMovie.Season, SelectedEpisode.EpisodeNo);
                });
            }
        }

        private void sp_OnFound(string url)
        {
            _mDispatcher.Invoke(new Action(() =>
            {
                var fbd = new FolderBrowserDialog();
                if (fbd.ShowDialog(App.Current.MainWindow.GetIWin32Window()) == DialogResult.OK)
                {
                    Utils.DownloadAndExtract(url, fbd.SelectedPath);
                }
            }), DispatcherPriority.Render);
        }

        #endregion EXTENSIONS
    }
}
